#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <ctype.h>

#define MAX_RECORDS 50

typedef struct {
	u_int64_t set_id;
	in_addr_t src_ip;
	in_addr_t real_src_ip;
	u_int16_t src_port;
	u_int16_t dst_port;	
	u_int64_t tx_time_ns;
	u_int64_t rx_time_ns;
	u_int64_t transit_time_ns;
} record_t;

typedef struct {
	u_int8_t record_count;
	record_t records[MAX_RECORDS];
} set_t;

#define SPOOFED(record) ((record)->src_ip != (record)->real_src_ip)

#define SRC 0
#define SRC_PORT 1
#define DST_PORT 2
#define SET_ID 3
#define REAL_SRC 4
#define TX_TIME 5
#define RX_TIME 6
#define TRANSIT_TIME 7
#define DONE 8

//141.149.218.208,10580,43404,0,141.149.218.208,1331776118038306613,1331776118169282625,130976012,
//src,src_port,dst_port,set_id,real_src_ip,tx_time,rx_time,transit_time

void print_record(record_t* record) {
	struct in_addr in;
	in.s_addr = record->src_ip;
	printf("Source IP Address: %s (%d)\n", inet_ntoa(in), in.s_addr);
}

record_t* parse_line(char* line) {
	//printf("%s\n", line);
	record_t* record = (record_t*)malloc(sizeof(record_t));
	char buffer[16];
	memset(buffer, 0, sizeof(buffer)); 
	int state = SRC;
	int field_start = 0;
	int i = 0;
	long long timestamp;
	int port;
		

	for (; line[i] && !isspace(line[i]); i++) {
		if (line[i] != ',') {
			continue;
		}
		////src,src_port,dst_port,set_id,real_src_ip,tx_time,rx_time,transit_time
		strncpy(buffer, line + field_start, i - field_start);
		switch (state) {
			case SRC:
				record->src_ip = inet_addr(buffer);
				break;
			case SRC_PORT:
				sscanf(buffer, "%d", &port);
				record->src_port = (u_int16_t)port;
				break;
			case DST_PORT:
				sscanf(buffer, "%d", &port);
				record->dst_port = (u_int16_t)port;
				break;
			case SET_ID:
				sscanf(buffer, "%lld", &timestamp);
				record->set_id = timestamp;
				break;
			case REAL_SRC:
				record->real_src_ip = inet_addr(buffer);
				break;
			case TX_TIME:
				sscanf(buffer, "%lld", &timestamp);
				record->tx_time_ns = timestamp;
				break;
			case RX_TIME:
				sscanf(buffer, "%lld", &timestamp);
				record->rx_time_ns = timestamp;
				break;
			case TRANSIT_TIME:
				sscanf(buffer, "%lld", &timestamp);
				record->transit_time_ns = timestamp;
				break;
		}
		field_start = i + 1;
		state++;
		memset(buffer, 0, sizeof(buffer));
	}
	if (state != DONE) {
		return NULL;
	}

	return record;
}

int reasonable(set_t* non_spoofed, set_t* spoofed) {
	return non_spoofed->record_count > 0
		  && spoofed->record_count > 0;
}

float set_latency(set_t* set) {
	u_int64_t started = set->records[0].tx_time_ns;
	u_int64_t ended = set->records[set->record_count - 1].rx_time_ns;
	return (ended - started) / 1000000000.0f;
}

float average_delay(set_t* set) {
	float total_delay = 0;
	int i;
	for (i = 0; i < set->record_count; i++) {
		total_delay += set->records[i].transit_time_ns / 1000000000.0f;
	}
	return total_delay / set->record_count;
}

char* ip_address_to_s(u_int32_t address) {
	struct in_addr in;
	in.s_addr = address;
	return inet_ntoa(in);
}

#define OPT_IP_INT 1
#define OPT_QUIET 2
#define OPT_COMMA 4

int main(int argc, char** argv) {
	if (argc < 2) {
		fprintf(stderr, "Usage: analyze max_sets [options]\n");
		exit(1);
	}

	int i;
	char buffer[1024];
	record_t* record;
	set_t* set;
	int max_sets;
	int hit_max = 0;
	sscanf(argv[1], "%d", &max_sets);
	int options = 0;
	char col_sep = '\t';
	char row_sep = '\n';

	if (argc > 2) {
		for (i = 0; i < strlen(argv[2]); i++) {
			switch (argv[2][i]) {
				case 'n':
					options |= OPT_IP_INT;
					break;
				case 'q':
					options |= OPT_QUIET;
					break;
				case 'c':
					col_sep = ',';
					break;
			}
		}
	}


	set_t* spoofed = calloc(max_sets, sizeof(set_t));
	set_t* non_spoofed = calloc(max_sets, sizeof(set_t));
	
	while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
		record = parse_line(buffer);
		if (record) {
			if (record->set_id >= max_sets) {
				hit_max = 1;
				break;
			}
			if (SPOOFED(record)) {
				set = &spoofed[record->set_id];
			}
			else {
				set = &non_spoofed[record->set_id];
			}
			memcpy(&(set->records[set->record_count]), record, sizeof(record_t));
			set->record_count++;
		}
	}

	int discarded_record_count = 0;
	if (!(options & OPT_QUIET)) {
		printf("set_id%cN_nsp%cN_sp%cM_nsp-M_sp%cD_transit%csrc_ip_addr%creal_src_ip_addr%c%c", col_sep, col_sep, col_sep, col_sep, col_sep, col_sep, col_sep, row_sep);
	}	
	struct in_addr in;
	for (i = 0; i < max_sets; i++) {
		if (!reasonable(&non_spoofed[i], &spoofed[i])) {
			discarded_record_count++;
			continue;
		}
		printf("%d%c%d%c%d%c%f%c%f%c",
			i, col_sep,
			non_spoofed[i].record_count, col_sep,
			spoofed[i].record_count, col_sep,
			average_delay(&spoofed[i]) - average_delay(&non_spoofed[i]), col_sep,
			set_latency(&spoofed[i]) - set_latency(&non_spoofed[i]), col_sep
		);

		if (options & OPT_IP_INT) {
			printf("%u%c%u%c", spoofed[i].records[0].src_ip, col_sep, spoofed[i].records[0].real_src_ip, col_sep);
		}
		else {
			in.s_addr = spoofed[i].records[0].src_ip;
			printf("%s%c", inet_ntoa(in), col_sep);
			in.s_addr = spoofed[i].records[0].real_src_ip;
			printf("%s%c", inet_ntoa(in), col_sep);
		}
		printf("%c",row_sep);
	}

	if (!(options & OPT_QUIET)) {
		printf("\nDiscarded %d records.\n", discarded_record_count);
		if (hit_max) {
			printf("WARNING: Not all records were read because the cutoff was set too low.\n");
		}
	}

	return 0;
}
